package com.xuecheng.search.service;

import com.xuecheng.framework.domain.cms.response.QueryResponseResult;
import com.xuecheng.framework.domain.course.CoursePub;
import com.xuecheng.framework.domain.course.TeachplanMedia;
import com.xuecheng.framework.domain.search.CourseSearchParam;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.search.pojo.CourseSearch;
import com.xuecheng.search.pojo.TeachplanMediaPub;
import com.xuecheng.search.respository.MediaSearchRepository;
import com.xuecheng.search.respository.SearchCourseRepository;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ShengJinFeng
 * @version V1.0
 * @ClassName EsCourseService
 * @Description (这里用一句话描述这个类的作用)
 * @date 2020/2/20 9:34
 * @since V1.0
 */
@Service
public class EsCourseService {

    private static final Logger LOGGER = LoggerFactory.getLogger(EsCourseService.class);

    @Value("${spring.data.elasticsearch.course_source_field}")
    private String course_source_field;

    @Autowired
    private SearchCourseRepository searchCourseRepository;

    @Autowired
    private MediaSearchRepository mediaSearchRepository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * @Description 课程搜索
     * @author ShengJinFeng
     * @date 2020/2/20
     */
    public QueryResponseResult list(int page, int size, CourseSearchParam courseSearchParam) {

        // 初始化自定义查询构建器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //构建布尔查询
        BoolQueryBuilder basicQuery = buildBoolQueryBuilder(courseSearchParam);
        queryBuilder.withQuery(basicQuery);
        //source源字段过虑
        String[] source_fields = course_source_field.split(",");
        queryBuilder.withSourceFilter(new FetchSourceFilter(source_fields, null));
        // 添加分页
        if (page <=0){
            page = 1;
        }
        if (size <=0){
            size = 10;
        }
        queryBuilder.withPageable(PageRequest.of(page-1,size));
        //高亮设置
        withHighlight(queryBuilder);
        AggregatedPage<CourseSearch> searchPage = elasticsearchTemplate.queryForPage(queryBuilder.build(), CourseSearch.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                List<T> courseSearchs = new ArrayList<>();
                SearchHits hits = searchResponse.getHits();
                for (SearchHit hit : hits) {
                    if (hits.getHits().length <= 0) {
                        return null;
                    }
                    CourseSearch courseSearch = new CourseSearch();
                    courseSearch.setId(hit.getId());
                    courseSearch.setName((String)hit.getSource().get("name"));
                    courseSearch.setPic(String.valueOf(hit.getSource().get("pic")));
                    courseSearch.setPrice(Float.parseFloat(String.valueOf(hit.getSource().get("price"))));
                    if(hit.getSource().get("price_old") != null){
                        courseSearch.setPrice_old(Float.parseFloat(String.valueOf(hit.getSource().get("price_old"))));
                    }
                    setHighLight(hit,"name",courseSearch);
                    courseSearchs.add((T) courseSearch);
                }
                //return new AggregatedPageImpl<>((List<T>)courseSearchs);
                AggregatedPage<T> result=new AggregatedPageImpl<T>(courseSearchs,pageable,searchResponse.getHits().getTotalHits());
                return result;
            }
        });

        List<CourseSearch> content = searchPage.getContent();
        //转化为List<CoursePub>
        List<CoursePub> list = convertCoursePub(content);
        QueryResult<CoursePub> queryResult = new QueryResult();
        queryResult.setTotal(searchPage.getTotalElements());
        queryResult.setList(list);
        return new QueryResponseResult(CommonCode.SUCCESS,queryResult);
    }

    /**
     * @Description 添加课程文档
     * @author ShengJinFeng
     * @date 2020/2/28
     */
    public ResponseResult addEsCourse(CoursePub coursePub) throws Exception {
        CourseSearch courseSearch = new CourseSearch();
        BeanUtils.copyProperties(coursePub,courseSearch);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(coursePub.getExpires())){
            Date expires = sdf.parse(coursePub.getExpires());
            courseSearch.setExpires_time(expires);
        }
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(coursePub.getPubTime())){
            Date pubTime = sdf.parse(coursePub.getPubTime());
            courseSearch.setPub_time(pubTime);
        }
        searchCourseRepository.save(courseSearch);
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * @Description 根据id查询课程信息
     * @author ShengJinFeng
     * @date 2020/3/5
     */
    public Map<String, CoursePub> getall(String id) {

        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("id",id);
        Iterable<CourseSearch> searchResult = searchCourseRepository.search(termQueryBuilder);
        Iterator<CourseSearch> iterator = searchResult.iterator();
        List<CourseSearch> courseSearches = IteratorUtils.toList(iterator);
        if (CollectionUtils.isEmpty(courseSearches)){
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        }
        CourseSearch courseSearch = courseSearches.get(0);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        CoursePub coursePub = new CoursePub();
        BeanUtils.copyProperties(courseSearch,coursePub);
        String dateFormat = sdf.format(courseSearch.getPub_time());
        coursePub.setPubTime(dateFormat);
        Map<String, CoursePub> map = new HashMap<>();
        map.put(id,coursePub);
        return map;
    }

    /**
     * @Description 添加课程计划媒资信息文档
     * @author ShengJinFeng
     * @date 2020/3/6
     */
    public ResponseResult addEsTeachplanMedia(List<TeachplanMedia> teachplanMedias) {
        List<TeachplanMediaPub> list = teachplanMedias.stream().map(teachplanMedia -> {
            TeachplanMediaPub tmp = new TeachplanMediaPub();
            BeanUtils.copyProperties(teachplanMedia, tmp);
            return tmp;
        }).collect(Collectors.toList());
        mediaSearchRepository.saveAll(list);
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * @Description 根据课程计划id查询媒资信息
     * @author ShengJinFeng
     * @date 2020/3/6
     */
    public TeachplanMedia getmedia(String teachplanId) {
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("teachplanId",teachplanId);
        Iterable<TeachplanMediaPub> iterable = mediaSearchRepository.search(termQueryBuilder);
        if (iterable == null || !iterable.iterator().hasNext()){
            return new TeachplanMedia();
        }
        TeachplanMediaPub teachplanMediaPub = iterable.iterator().next();
        TeachplanMedia teachplanMedia = new TeachplanMedia();
        BeanUtils.copyProperties(teachplanMediaPub,teachplanMedia);
        return teachplanMedia;
    }

    /**
     * @Description 添加高亮条件
     * @author ShengJinFeng
     * @date 2020/2/28
     */
    private void withHighlight(NativeSearchQueryBuilder searchQueryBuilder){
        HighlightBuilder.Field hfield= new HighlightBuilder.Field("name")
                .preTags("<span style=\"color:red\">")
                .postTags("</span>")
                .fragmentSize(100);
        searchQueryBuilder.withHighlightFields(hfield);
    }

    /**
     * @Description 反射调用属性的set方法设置高亮的内容
     * @author ShengJinFeng
     * @date 2020/2/28
     */
    private void setHighLight(SearchHit searchHit, String field, Object object) {
        Map<String, HighlightField> highlightFieldMap = searchHit.getHighlightFields();
        HighlightField highlightField = highlightFieldMap.get(field);
        if (highlightField != null) {
            String highLightMessage = highlightField.fragments()[0].toString();
            String capitalize = StringUtils.capitalize(field);
            String methodName = "set"+capitalize;
            Class<?> clazz = object.getClass();
            try {
                Method setMethod = clazz.getMethod(methodName, String.class);
                setMethod.invoke(object, highLightMessage);

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * @Description 转化为List<CoursePub>
     * @author ShengJinFeng
     * @date 2020/2/22
     */
    private List<CoursePub> convertCoursePub(List<CourseSearch> content) {
        List<CoursePub> list = new ArrayList<>();
        content.forEach(cs ->{
            CoursePub cp = new CoursePub();
            BeanUtils.copyProperties(cs,cp);
            list.add(cp);
        });
        return list;
    }

    /**
     * @Description 构建布尔查询
     * @author ShengJinFeng
     * @date 2019/12/30
     */
    private BoolQueryBuilder buildBoolQueryBuilder(CourseSearchParam courseSearchParam) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotEmpty(courseSearchParam.getKeyword())){
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(courseSearchParam.getKeyword(), "name", "teachplan", "description")
                    .minimumShouldMatch("70%")
                    .field("name",10);
            boolQueryBuilder.must(multiMatchQueryBuilder);
        }
        //添加过滤条件
        if(StringUtils.isNotEmpty(courseSearchParam.getMt())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("mt",courseSearchParam.getMt()));
        }
        if(StringUtils.isNotEmpty(courseSearchParam.getSt())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("st",courseSearchParam.getSt()));
        }
        if(StringUtils.isNotEmpty(courseSearchParam.getGrade())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("grade",courseSearchParam.getGrade()));
        }
        return boolQueryBuilder;
    }
}
